package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AssertUtil;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private TrackStatMapper trackStatMapper;
    @Autowired
    private VodService vodService;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private AlbumStatMapper albumStatMapper;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveTrackInfo(TrackInfo trackInfo) {
        // 保存声音信息
        // 设置保存声音的基本信息 size type .. 通过Vod请求获取
        TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
        // 设置用户id
        trackInfo.setUserId(AuthContextHolder.getUserId());
        // 设置声音属性相关的值 size type
        trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        trackInfo.setMediaType(trackMediaInfoVo.getType());
        trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()));
        // 设置声音的序号 可以基于上一个声音的序号 +1
        // 设置声音的序号 如果不存在则初始化 如果存在则+1
        // 查询放当前专辑最大的声音序号
        Long maxOrderNum = trackInfoMapper.selectMaxOrderNum(trackInfo.getAlbumId());
        trackInfo.setOrderNum(maxOrderNum == null ? 1 : maxOrderNum + 1);
        // 审核 来源
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        // 默认审核通过 待完善
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        // 保存基本信息
        trackInfoMapper.insert(trackInfo);
        // 2. 保存声音的统计信息
        insertTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);
        insertTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        insertTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        insertTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        // 3. 专辑的声音数量+1
        albumInfoMapper.updateAlbumTrackCount(trackInfo.getAlbumId(), 1);
    }

    @Override
    public Page<TrackListVo> findUserTrackPage(Long pageNum, Long limitSize, TrackInfoQuery trackInfoQuery) {

        // 构建查询条件 传入用户id
        trackInfoQuery.setUserId(AuthContextHolder.getUserId());
        Page<TrackListVo> page = trackInfoMapper.findUserTrackPage(new Page<>(pageNum, limitSize), trackInfoQuery);

        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeTrackInfo(Long id) {

        // 查询声音信息 作为缓存
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        // 1. 删除声音的基本信息
        trackInfoMapper.deleteById(id);
        // 2. 删除声音的统计信息
        trackStatMapper.delete(new LambdaUpdateWrapper<TrackStat>().eq(TrackStat::getTrackId, id));
        // 3. 专辑声音数量-1
        // 根据专辑id
        albumInfoMapper.updateAlbumTrackCount(trackInfo.getAlbumId(), -1);
        // 4. 删除vod中的声音
        vodService.removeTrackFile(trackInfo.getMediaFileId());
    }

    // 修改声音信息
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTrackInfo(Long id, TrackInfo trackInfo) {
        // 1. 查询修改之前的声音  mediaFileId
        TrackInfo trackInfoDB = trackInfoMapper.selectById(id);
        String mediaFileId = trackInfoDB.getMediaFileId();
        // 判断前端页面声音是否被修改了
        if (!mediaFileId.equals(trackInfo.getMediaFileId())) {

            // 2.  声音被修改了,删除vod中的声音
            vodService.removeTrackFile(mediaFileId);
            // 3. 获新上传的声音信息 设置给trackInfo
            TrackMediaInfoVo mediaInfoVo = vodService.getMediaInfo(trackInfo.getMediaFileId());
            trackInfo.setMediaSize(mediaInfoVo.getSize());
            trackInfo.setMediaDuration(new BigDecimal(mediaInfoVo.getDuration()));
            trackInfo.setMediaType(mediaInfoVo.getType());
            trackInfo.setMediaUrl(mediaInfoVo.getMediaUrl());
        }
        // 4. 修改声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    @Override
    public Page<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Long pageNum, Long pageSize) {
        // 专辑详情页查询声音的分页列表数据 根据orderNumber升序排序
        // 1. 查询专辑的声音列表数据
        Page<AlbumTrackListVo> page = trackInfoMapper.selectAlbumTrackPage(new Page<AlbumTrackListVo>(pageNum, pageSize), albumId);
        // 包含 track_info表 track_stat表 声音的付费标识(复杂) user_paid_track表
        // 声音付费表示的设置
        // 试听
        // 2. 查询声音的所属专辑,获取专辑的收费类型
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        // 用户id
        Long userId = AuthContextHolder.getUserId();
        // 用户未登录
        if (userId == null) {
            // 如果专辑不是免费的,且声音序号号大于免费试听的设置声音的付费标识为true
            if (!albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
                page.getRecords().forEach(item -> {
                    if (item.getOrderNum().intValue() > albumInfo.getTracksForFree().intValue()) {
                        item.setIsShowPaidMark(true);
                    }
                });
            }
        } else if (userId != null) {
            if (albumInfo.getUserId() != userId) {
                // 用户登录
                // 1. 获取用户信息
                Result<UserInfoVo> userInfoResult = userInfoFeignClient.getUserInfoById(userId);
                AssertUtil.resultAssert(userInfoResult, ResultCodeEnum.DATA_ERROR);
                UserInfoVo userInfoVo = userInfoResult.getData();
                // 2. 远程调用获取该用户已经购买的专辑列表 远程调用user服务
                Result<Boolean> buyAlbumResult = userInfoFeignClient.isBuyAlbum(userId, albumId);
                AssertUtil.resultAssert(buyAlbumResult, ResultCodeEnum.DATA_ERROR);
                Boolean buyAlbum = buyAlbumResult.getData();
                // 3. 远程调用 判断用户是否已经购买该专辑的声音 获取用户已经购买的声音列表
                Result<List<UserPaidTrack>> userPaidTrackResult = userInfoFeignClient.getUserBuyTrackIdList(userId, albumId);
                AssertUtil.resultAssert(userPaidTrackResult, ResultCodeEnum.DATA_ERROR);
                List<UserPaidTrack> userPaidTrackList = userPaidTrackResult.getData();
                // 判断用户是否为vip
                // 3. 如果用户为vip,则仅有收费的且>免费试听级数的才收费
                if (isVipUser(userInfoVo)) {
                    // 如果用户为vip,则仅有收费的且>免费试听级数的才收费
                    page.getRecords().forEach(item -> {
                        if (item.getOrderNum().intValue() > albumInfo.getTracksForFree().intValue()
                                && albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_REQUIRE)) {
                            item.setIsShowPaidMark(true);
                        }
                    });
                } else {
                    // 4. 如果用户不是vip,则非免费且大于试听的都收费
                    page.getRecords().forEach(item -> {
                        if (item.getOrderNum().intValue() > albumInfo.getTracksForFree().intValue()) {
                            item.setIsShowPaidMark(true);
                        }
                    });
                }
                // 3. 判断用户是否已经购买该专辑 如果已经购买 则收费标识设置为false
                if (buyAlbum) {
                    page.getRecords().forEach(
                            item -> {
                                item.setIsShowPaidMark(false);
                            }
                    );
                    // 4, 用户没买该专辑判断用户是否该该专辑的声音
                } else if (userPaidTrackList.size() > 0) {
                    page.getRecords().forEach(
                            item -> {
                                if (userPaidTrackList.stream().anyMatch(
                                        userPaidTrack -> userPaidTrack.getTrackId().equals(item.getTrackId())
                                )) {
                                    item.setIsShowPaidMark(false);
                                }
                            }
                    );
                }
            }
        }
        return page;
    }

    // 处理MQ 消息 更新播放量
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {

        Long trackId = trackStatMqVo.getTrackId();
        String statType = trackStatMqVo.getStatType();
        Integer count = trackStatMqVo.getCount();
        // 1. 获取声音的信息
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        // 2. 更新声音播放量
        trackStatMapper.updateTrackStat(trackId, statType, count);
        // 3. 更新声音播放量
        if (SystemConstant.TRACK_STAT_PLAY.equals(statType)) {
            albumStatMapper.updateAlbumStat(trackInfo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY, count);
        }


    }

    // 查询用户购买声音的所有支付方式
    @Override
    public List<TrackOrderVo> findUserTrackPaidList(Long id) {
        List<TrackOrderVo> list = new ArrayList<>();
        // 1. 根据声音id 查询 声音 ,获取专辑id查询专辑
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        AssertUtil.isNull(trackInfo);
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        AssertUtil.isNull(albumInfo);
        if (albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
            TrackOrderVo trackOrderVo = new TrackOrderVo();
            trackOrderVo.setName("全部免费");
            trackOrderVo.setPrice(BigDecimal.ZERO);
            trackOrderVo.setTrackCount(10);
            list.add(trackOrderVo);
            return list;
        }
        // 2. 查询本专辑和本专辑orderNum以后的所有声音列表 获取TrackId
        List<Long> trackIds = trackInfoMapper.selectList(new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, albumInfo.getId())
                .ge(TrackInfo::getOrderNum, trackInfo.getOrderNum())).stream().map(TrackInfo::getId).toList();

        // 3. 查询用户已经购买的本专辑列表声音列表
        // 可以改用 Set 存储已购买ID，因为 Set.contains() 的时间复杂度是 O(1)。
        Long userId = AuthContextHolder.getUserId();
        Result<List<UserPaidTrack>> userPaidTrackResult = userInfoFeignClient.getUserBuyTrackIdList(userId, albumInfo.getId());
        AssertUtil.resultAssertMayNull(userPaidTrackResult, ResultCodeEnum.DATA_ERROR);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackResult.getData();

        // 4, 选出用户未购买的声音id列表  计算出用户还可以购买的声音数量
        if (!CollectionUtil.isEmpty(userPaidTrackList)) {
            List<Long> isBuyIds = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).toList();
            trackIds = trackIds.stream().filter(
                    buyId -> !isBuyIds.contains(buyId)
            ).toList();
        }
        // 5. 生成购买方案

        // todo 会员免费
        if (albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_VIPFREE)) {
            list.add(new TrackOrderVo().setName("VIP会员免费").setPrice(BigDecimal.ZERO).setTrackCount(0));
        }
        // 购买本级
        list.add(new TrackOrderVo().setName("本级").setPrice(albumInfo.getPrice()).setTrackCount(0));
        int count = trackIds.size();

        // 判断
        if (count > 5) {
            list.add(new TrackOrderVo().setName("后5级").setPrice(albumInfo.getPrice().multiply(BigDecimal.valueOf(5))).setTrackCount(5));
        }
        if (count > 10) {
            list.add(new TrackOrderVo().setName("后10级").setPrice(albumInfo.getPrice().multiply(BigDecimal.valueOf(10))).setTrackCount(10));
        }
        if (count > 20) {
            list.add(new TrackOrderVo().setName("后20级").setPrice(albumInfo.getPrice().multiply(BigDecimal.valueOf(10))).setTrackCount(20));
        }
        if (count > 0) {
            list.add(new TrackOrderVo().setName("后" + (count) + "级").setPrice(albumInfo.getPrice().multiply(BigDecimal.valueOf(count))).setTrackCount(count));
        }
        return list;
    }

    // 远程调用根据 专辑声音查询需要购买的声音列表
    @Override
    public List<TrackInfo> getNeedTrackInfoList(Long albumId, Long trackNo, Integer count) {

        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(new LambdaQueryWrapper<TrackInfo>()
                .eq(TrackInfo::getAlbumId, albumId)
                .ge(TrackInfo::getOrderNum, trackNo)
                .orderByAsc(TrackInfo::getOrderNum)
                .last("limit " + count));
        return trackInfoList;
    }

    @Override
    public TrackStatVo getTrackStatVo(Long id) {
        TrackStatVo trackStatVo = trackInfoMapper.getTrackStatVo(id);
        return trackStatVo;
    }

    private void insertTrackStat(Long trackId, String statType) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(0);
        trackStatMapper.insert(trackStat);
    }

    // 判断用户是否为vip用户
    public boolean isVipUser(UserInfoVo userInfoVo) {
        if (userInfoVo == null || userInfoVo.getVipExpireTime() == null) {
            return false;
        }
        return 1 == userInfoVo.getIsVip().intValue()
                && userInfoVo.getVipExpireTime().after(new Date());
    }
}
